require 'esi/cache'
require 'esi/tag/base'
require 'esi/tag/include'
require 'esi/tag/attempt'
require 'esi/tag/except'
require 'esi/tag/try'
require 'esi/tag/invalidate'
require 'rubygems'
gem 'libxml-ruby'
require 'xml/libxml'

module ESI

  class Parser
    attr_accessor :max_depth, :include_parser

    def initialize(cache,router,proxy_headers,mongrel_http_params)
      @output = $stdout
      @esi_tag = nil
      @cache = cache
      @router = router
      @proxy_headers = proxy_headers
      @mongrel_http_params = mongrel_http_params
      @max_depth = 3
      @include_parser = nil
    end

    def output=(o)
      @output = o
    end

    def parse(proxy_input)
      parser = XML::SaxParser.new
      parser.string = proxy_input
      parser.callbacks = self
      parser.parse
    end

    def on_internal_subset(a,b,c)
      buffer "<!DOCTYPE #{a} PUBLIC \"#{b}\" \"#{c}\">\n"
    end

    def on_external_subset(a,b,c)
    end
      
    def on_processing_instruction(target, data)
      buffer target
    end

    def on_cdata_block(cdata)
      # NOTE: the CDATA section must wrap the contents of the <esi:invalidate tag
      if @esi_tag and @esi_tag.name == 'invalidate'
        buffer cdata
      else
        buffer "<![CDATA[#{cdata}]]>"
      end
    end
    
    def on_start_document
    end

    def on_end_document
    end

    def on_start_element( name, attrs )
      if( name.match(/esi:/) )
        tag = ESI::Tag::Base.create(@router, @proxy_headers, @mongrel_http_params, name, attrs, @cache)
        tag.include_parser = @include_parser
        if @esi_tag
          @esi_tag.add_child(tag)
        else
          @esi_tag = tag
        end
      else
        buffer "<#{name}#{print_attrs(attrs)}>"
      end
    end

    def on_end_element( name )
      if( name.match(/esi:/) )
        if @esi_tag.name == (name.gsub(/esi:/,''))
          @esi_tag.close(@output)
          @esi_tag = nil
        else
          @esi_tag.close_child(@output,name)
        end
      else
        buffer "</#{name}>"
      end
    end
    
    def on_characters( chars )
      buffer chars
    end

    def on_comment( comment )
      buffer "<!--#{comment}-->"
    end

    def on_parser_error( error )
      $stderr.puts error
    end

    def print_attrs(attrs)
      " " + attrs.collect{|k,v| "#{k}=\"#{v}\"" }.join(' ') unless attrs.empty?
    end

    def buffer(string)
      if @esi_tag
        @esi_tag.buffer( @output, string )# unless string.strip.empty?
      else
        @output << string
      end
    end

  end
end
